package zhu.common.strutsext;


import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.Locale;

import javax.servlet.ServletContext;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.log4j.Logger;
import org.apache.struts2.views.freemarker.FreemarkerManager;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.WebApplicationContext;

import freemarker.cache.MruCacheStorage;
import freemarker.core.Environment;
import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModel;

/**
 * @author Alex Zhang(alexzhang@sohu-inc.com)
 * 对freemarker页面的解析类
 *
 */
public class ExtendedFreemarkerManager extends FreemarkerManager {
	
	final Logger log = Logger.getLogger(ExtendedFreemarkerManager.class);
	private ApplicationContext context;
	
	public static boolean DEBUG = false;
	
	protected Configuration createConfiguration(ServletContext servletContext) 
		throws TemplateException {  
		
		Configuration config = super.createConfiguration(servletContext);  
		config.setEncoding(Locale.getDefault(), "utf-8");
        config.setNumberFormat("##.##");
        config.setDateFormat("yyyy-MM-dd HH:mm:ss");
        config.addAutoImport("html", "/common/macros.ftl");
        //config.addAutoImport("monitor", "/monitor/event/monitor_macros.ftl");

        if (!DEBUG) {
        	config.setCacheStorage(new MruCacheStorage(20, 50));
	        config.setTemplateExceptionHandler(new TemplateExceptionHandler() {
				
				public void handleTemplateException(TemplateException te,
						Environment evn, Writer out) throws TemplateException {
					try {
						log.error("template exception:", te);
						out.write("500 SERVICE EXCEPTION");
					} catch (IOException ex) {
						throw new TemplateException(ex, evn);
					}
				}
			});
        }
        
		try {
			context = (ApplicationContext)servletContext
					.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
			
            BeansWrapper wrapper = new BeansWrapper();
            wrapper.setExposureLevel(BeansWrapper.EXPOSE_ALL);
            org.apache.commons.configuration.Configuration methodsConfig = new PropertiesConfiguration("struts-methods.properties");
            
            for (Iterator it = methodsConfig.getKeys(); it.hasNext();) { 
            	String key = (String) it.next();
            	String value = methodsConfig.getString(key);
                TemplateHashModel staticModels = wrapper.getStaticModels();
                Object bean = null;
                if (isSpringBean(value)) {
                    bean = context.getBean(value.substring("spring:".length(), value.length()));
                }else{
                    Class clazz = Class.forName(value);
                    if (TemplateModel.class.isAssignableFrom(clazz)) {
                        bean = clazz.newInstance();
                    }else{
                        bean = staticModels.get(value);
                    }
                }
                if (bean == null) {
                    log.error("Error caused while loading freemarker static settings: bean:[" + key + "] is null");
                    continue;
                }
                if (bean instanceof TemplateModel) {
                    config.setSharedVariable(key, (TemplateModel)bean);
                }else{
                    config.setSharedVariable(key, bean);
                }
            }
		} catch (TemplateException ex) {
            log.error("initialize freemarker configuration error.", ex);
        } catch (Exception ex) {
            log.error("Error while loading freemarker settings from /struts-methods.properties", ex);
        } 
        return config;
	}  
	
	private boolean isSpringBean(String name) {
        if (name.startsWith("spring:")) {
            return context.containsBean(name.substring("spring:".length(), name.length()));
        }else{
            return false;
        }
    }
}
